<?php

/**
 * 公共函数定义文件
 *
 */

if (!function_exists("show")) {
    /**
     * API接口使用，返回带code,msg,data的json
     * @author MrGuo
     * @date   2020-12-19
     * @param  int|integer $code [返回码 200成功 400及以上失败，不要用0]
     * @param  string      $msg  [提示信息，必须直观]
     * @param  [type]      $data [返回数据]
     * @param  [bool]      $encrypt [是否进行数据加密]
     * @return [json]      [json]
     */
    function show(int $code = 200, string $msg = "请求成功", $data = null, $encrypt = false) {
        $result=array("data" => $data, "msg" => $msg, "code" => $code,'encrypt'=>false);
        if($encrypt&&!empty($result['data'])){
            $result['data']=aes_encrypt($data);
            $result['encrypt']=true;
        }
        return response()->json($result);
    }
    /**
     * 参数顺序不变，少了第一个code
     * 不建议在业务判断较强的地方使用，code一样，前端不好处理
     */
    function show_success(string $msg = "请求成功", $data = null, $encrypt = false) {
        return show(200,$msg, $data, $encrypt);
    }
    /**
     * 参数顺序不变，少了code和encrypt
     * 不建议在业务判断较强的地方使用，code一样的前端不好处理
     */
    function show_error(string $msg = "请求异常", $data = null) {
        return show(500,$msg, $data);
    }
}

if (!function_exists("unique_no")) {
    /**
     * 生成唯一订单号
     * @author MrGuo
     * @param  string     $prefix  [订单号前缀]
     * @param  int        $randNum [结尾自定义随机字符串]
     * @return string              [订单号，默认20位]
     */
    function unique_no(string $prefix = "", int $randNum = null) {
        $snowId = new \Godruoyi\Snowflake\Snowflake();
        return strtoupper($prefix) . date("Y") . $snowId->id() . ($randNum ?? '');
    }
}
if(!function_exists('pluck_array')){
    /**
     * 将二维数组转为一维键值对数组，此方法主要方便后台在模型的pluck方法不能满足时使用
     * @param array $array
     * @param string $label
     * @param string $id
     * @return array
     */
    function pluck_array(array $array,string $label="name",string $id='id'){
        $pluckList=[];
        foreach($array as $v){
            if(isset($v[$id])&&isset($v[$label])){
                $pluckList[$v[$id]]=$v[$label];
            }
        }
        return $pluckList;
    }
}
if(!function_exists('send_sms')){
    /**
     * @param string $action 发送指令。例如：regist,login,confirm, 与/config/sms.php里面的templates的键名匹配
     * @param string $mobile 手机号
     * @param array $data 模板中的变量，例如：['code'=>'156879']
     * @param bool $isCode 是否为验证码类型，是将缓存5分钟
     * @return array|\Illuminate\Http\JsonResponse
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function send_sms(string $action,string $mobile,array $data,bool $isCode=false)
    {
        $config=config("easysms");
        $easySms = new \Overtrue\EasySms\EasySms($config);
        if(isset($config['templates'][$action])){
            $template_code=$config['templates'][$action]['template_code'];
            $content=$config['templates'][$action]['content'];
            foreach($data as $k=>$v){//替换变量
                $content=str_ireplace('${'.$k.'}',$v,$content);
            }
            try{
                $res= $easySms->send($mobile, [
                    'content'  => $content,
                    'template' => $template_code,
                    'data' =>$data,
                ]);
                if($res&&$isCode&&isset($data['code'])){
                    cache()->set("sms_".$action."_".$mobile,$data['code'],300);
                }
            }catch(\Exception $e){
                return show_error("发送失败",['error'=>$e->getMessage()]);
            }
            return show_success('发送成功');
        }else{
            return  show_error("发送失败",['error'=>'未找到模板']);
        }
    }
    function check_sms_code(string $action,string $mobile,int $code): bool
    {
        $cacheCode=cache()->get("sms_".$action."_".$mobile);
        if(intval($cacheCode)===$code){
            cache()->forget("sms_".$action."_".$mobile);
            return true;
        }else{
            return false;
        }
    }
}

if (!function_exists("unique_bigint")) {
    /**
     * 生成唯一的Bigint数字ID，分布式系统中使用，每台主机配置不同的datacenter和workerid
     * @param int|null $datacenter [数据中心ID,1-5位]
     * @param int|null $workerid [机器ID,1-5位]
     * @param int|null $randNum [自定义随机数，不加默认返回16位]
     * @return string [string]               [唯一ID]
     * @throws Exception
     * @author MrGuo
     * @date   2020-12-19
     */
    function unique_bigint(int $datacenter = null, int $workerid = null, int $randNum = null) {
        $snowId = new \Godruoyi\Snowflake\Snowflake($datacenter, $workerid);
        $snowId->setStartTimeStamp(strtotime(date("Y-01-01 00:00:00")));
        return $snowId->id() . ($randNum ?? '');
    }
}
if (!function_exists("uuid")) {
    /**
     * Laravel的UUID
     * @author MrGuo
     * @date   2020-12-19
     * @return string     [uuid]
     */
    function uuid() {
        return \Illuminate\Support\Str::uuid();
    }
}
if(!function_exists('payload_encrypt')){
    /**
     * @param array $data 加密数据,建议只携带关键信息，避免数据过大
     */
    function payload_encrypt(array $data): string
    {
        if(!isset($data['timestamp']))$data['timestamp']=time();
        $token=\Illuminate\Support\Facades\Crypt::encrypt($data,true);
        return urlencode($token);
    }
}
if(!function_exists('payload_decrypt')){
    /**
     * 从header中取得关键数据
     * @param string|null $key 直接取出的字段，为空时返回整个数组
     * @param string $header
     * @return array
     */
    function payload_decrypt(string $key=null,string $header="Authorization"): ?array
    {
        try {
            $token=request()->header($header);
            if(is_array($token)) {
                $token = $token[0];
            }
            $decoded=\Illuminate\Support\Facades\Crypt::decrypt(urldecode($token),true);
            if($key){
                return $decoded[$key]??null;
            }
            return $decoded;
        }catch(\Exception $e){
            return [];
        }
    }
}

if (!function_exists("validate_form")) {
    /**
     * 表单批量验证
     * @param array $fields 数组，
     * @param array|null $data
     * @param bool $exit 验证不通过是否直接exit，默认关闭
     * @return string||true            错误信息，验证通过返回true
     * @demo   validate_form(["mobile|手机号" => "required|size:11","name|姓名" => "required"])
     * @author MrGuo
     * @date   2020-12-21
     */
    function validate_form(array $fields = [],array $data=null, bool $exit = true) {
        $rules = [];
        $labels = [];
        $error = false;
        $datas=empty($data)?request()->all():$data;
        try {
            foreach ($fields as $key => $rule) {
                $arr = explode("|", $key);
                $labels[$arr[0]] = $arr[1] ?? $arr[0];
                $rules[$arr[0]] = $rule;
            }
            $validator = \Illuminate\Support\Facades\Validator::make($datas, $rules, [], $labels);
            if ($validator->errors()->first()) {
                $error = $validator->errors()->first();
            }
        } catch (\Exception $e) {
            $error = "表单验证规则错误";
        }
        if ($error !== false) {
            if ($exit) {
                header("Content-Type:application/json");
                echo json_encode(['code' => 600, 'msg' => $error], JSON_UNESCAPED_UNICODE);
                exit;
            }
            return $error;
        }
        return true;
    }
}

if (!function_exists("children_tree")) {
    /**
     *  递归二叉树
     * @param array $son [二维数组，主键名是id]
     * @param string $field
     * @param boolean|null $alone [是否返回没有找到上级的, 默认返回]
     * @return array [array]             [N维数组，children是子集，level表示第几级]
     * @author MrGuo
     * @date   2020-12-19
     */
    function children_tree(array $son = [], string $field = "pid", bool $alone = null) {
        $father = [];
        foreach ($son as $key => $val) {
            if (intval($val[$field]) == 0) {
                //获取第一代
                array_push($father, $val);
                unset($son[$key]);
            }
        }
        foreach ($father as $key => $val) {
            $father[$key] = __Children($val, $son, 0);
        }
        if ($alone) {
            //保留没有父级的孤儿
            foreach ($son as $key => $val) {
                array_push($father, $val);
            }
        }
        return $father;
    }
    function __Children(array $father = [], array $son = [], int $level = 0, string $field = "pid") {
        $father['children'] = array();
        foreach ($son as $k => $v) {
            if (intval($v[$field]) == intval($father['id'])) {
                $sons = __Children($v, $son, $level + 1, $field); //递归，把一个系先全部找完。
                array_push($father['children'], $sons);
                unset($son[$k]);
            }
        }
        $father['level'] = $level;
        return $father;
    }
}

if (!function_exists('curl_post')) {
    /**
     * [发送curl的POST请求]
     * @author MrGuo
     * @date   2020-12-19
     * @param  string $url    [请求链接]
     * @param  array  $data   [参数，字符串或者数组]
     * @param  array  $header [请求Header]
     * @return array         [false表示失败]
     */
    function curl_post(string $url = '', $data = [], array $header = []) {
        $ch = curl_init();
        // $header=[
        //      'Content-Type:'.'application/x-www-form-urlencoded; charset=UTF-8'
        // ];
        // curl_setopt($ch, CURLOPT_SSLVERSION, 1);//检验SSL
        // if (defined('CURL_SSLVERSION_TLSv1')) {
        //  curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
        // }
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if (!empty($header)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
            curl_setopt($ch, CURLOPT_HEADER, 0); //返回response头部信息
        }
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, gettype($data) == 'string' ? $data : http_build_query($data));
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $rs = curl_exec($ch);
        if (curl_errno($ch)) {
            //出错则显示错误信息
            //var_dump( curl_error($ch) );
            return false;
        } else {
            curl_close($ch);
            //默认了进行了json转换
            $arr = json_decode($rs, true);
            if ($arr) {
                return $arr;
            }
            return $rs;
        }
    }
}
if (!function_exists('curl_get')) {
    /**
     * [发送curl的GET请求]
     * @author MrGuo
     * @date   2020-12-19
     * @param  string $url    [请求链接]
     * @param  array  $data   [参数, 字符串或者数组]
     * @param  array  $header [请求Header]
     * @return array         [false表示失败]
     */
    function curl_get(string $url = '', $data = "", array $header = []) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        if (!empty($header)) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
            curl_setopt($ch, CURLOPT_HEADER, 0); //返回response头部信息
        }
        if ($data != '') {
            $url .= "?" . (gettype($data) == 'string' ? $data : http_build_query($data));
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $rs = curl_exec($ch);
        if (curl_errno($ch)) {
            //出错则显示错误信息
            //var_dump( curl_error($ch) );
            return false;
        } else {
            curl_close($ch);
            //默认了进行了json转换
            $arr = json_decode($rs, true);
            if ($arr) {
                return $arr;
            }
            return $rs;
        }
    }
}

if (!function_exists('admin_config')) {
    /**
     * 获取系统配置表配置，注意，此方法使用了Cache
     * @author MrGuo
     * @date   2020-12-19
     * @param  string     $key [字段名]
     * @return [type]          [description]
     */
    function admin_config(string $key = '') {
        $config = \App\Models\AdminConfig::getConfig($key);
        return $config;
    }
}

if (!function_exists('rand_char')) {
    /**
     * 生成随机字符串
     * @param int $length 生成长度
     * @param int $type 生成类型：0-小写字母+数字，1-小写字母，2-大写字母，3-数字，4-小写+大写字母，5-小写+大写+数字
     * @return string
     */
    function rand_char($length = 8, $type = 5) {
        $a = 'abcdefghijklmnopqrstuvwxyz';
        $A = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $n = '0123456789';

        switch ($type) {
            case 1:$chars = $a;
                break;
            case 2:$chars = $A;
                break;
            case 3:$chars = $n;
                break;
            case 4:$chars = $a . $A;
                break;
            case 5:$chars = $a . $A . $n;
                break;
            default:$chars = $a . $n;
        }

        $str = '';
        for ($i = 0; $i < $length; $i++) {
            $str .= $chars[mt_rand(0, strlen($chars) - 1)];
        }
        return $str;
    }
}
/**
 * 数字装换汉字，例如：25 转换 成二十五
 * @param int $num
 * @return string
 */
function number2chinese($num): string
{
    if (is_int($num) && $num < 100) {
        $char = array('零', '一', '二', '三', '四', '五', '六', '七', '八', '九');
        $unit = ['', '十', '百', '千', '万'];
        $return = '';
        if ($num < 10) {
            $return = $char[$num];
        } elseif ($num%10 == 0) {
            $firstNum = substr($num, 0, 1);
            if ($num != 10) $return .= $char[$firstNum];
            $return .= $unit[strlen($num) - 1];
        } elseif ($num < 20) {
            $return = $unit[substr($num, 0, -1)]. $char[substr($num, -1)];
        } else {
            $numData = str_split($num);
            $numLength = count($numData) - 1;
            foreach ($numData as $k => $v) {
                if ($k == $numLength) continue;
                $return .= $char[$v];
                if ($v != 0) $return .= $unit[$numLength - $k];
            }
            $return .= $char[substr($num, -1)];
        }
        return $return;
    }
}
if(!function_exists('array_sort_by')){
    /**
     * 二位数组重新排序
     * @param array $array 数组
     * @param string $k1 要排序的字段
     * @param int $sort 排序方式：3降序，4升序    参考：https://php.net/manual/en/array.constants.php
     * @param string|null $k2 要排序的字段2（可选）
     * @param int $sort2 排序方式2：3降序，4升序   参考：https://php.net/manual/en/array.constants.php
     * @return array
     */
    function array_sort_by(array $array, string $k1, int $sort = SORT_DESC, string $k2=null, int $sort2=SORT_DESC) {
        $args = func_get_args();
        if(empty($args)){
            return null;
        }
        $arr = array_shift($args);
        if(!is_array($arr)){
            throw new Exception("第一个参数不为数组");
        }
        foreach($args as $key => $field){
            if(is_string($field)){
                $temp = array();
                foreach($arr as $index=> $val){
                    $temp[$index] = $val[$field];
                }
                $args[$key] = $temp;
            }
        }
        $args[] = &$arr;//引用值
        call_user_func_array('array_multisort',$args);
        return array_pop($args);
    }
}

if(!function_exists('distance_api')){
    /**
     * 腾讯地图获取坐标点位置,1对多
     * @param float $latitude 起点纬度
     * @param float $longitude 起点经度
     * @param array $points 所有终点坐标，[['latitude'=>30.213123784,'longitude'=>'108.23861873']]
     * @param string $mode 导航方式，driving：驾车，walking：步行 ，bicycling：自行车
     * @return array $result 返回二维数组
     */
    function distance_api(float $latitude,float $longitude,array $points,string $mode='walking'): array
    {
        $to=[];
        $result=[];
        foreach($points as $k=>$v){
            $to[]=$v['latitude'].",".$v['longitude'];
            $result[$k]=[
                'distance'=>null,
                'duration'=>null,
                //表示从起点到终点的结合路况的时间，秒为单位
                //注：步行/骑行方式（不计算耗时）以及起终点附近没有道路造成无法计算时，不返回本此节点
            ];
        }
        $to=implode(';',$to);
        $url="https://apis.map.qq.com/ws/distance/v1/matrix/?mode={$mode}&from={$latitude},{$longitude}&to={$to}&key=".env("TENCENT_MAP_API_KEY");

        try{
            $res=curl_get($url);

            $result=$res['result']['rows'][0]['elements'];
            return $result;
        }catch(\Exception $e){
            return $result;
        }
    }
}
if(!function_exists('distance_line')){
    /**
     * 计算两点地理坐标之间的距离
     * @param float $longitude1 起点经度
     * @param float $latitude1 起点纬度
     * @param float $longitude2 终点经度
     * @param float $latitude2 终点纬度
     * @param Int $unit 单位 1:米 2:公里
     * @param Int $decimal 精度 保留小数位数
     * @return float
     */
    function distance_line($longitude1, $latitude1, $longitude2, $latitude2, $unit = 2, $decimal = 2): float
    {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI = 3.1415926;
        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;
        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI / 180.0;
        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;
        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;
        if ($unit == 2) {
            $distance = $distance / 1000;
        }
        return round($distance, $decimal);
    }
}
if(!function_exists('distance_line_raw')){
    /**
     * 生成MySQL坐标距离字段
     * @param string $latitude 起点纬度
     * @param string $longitude 起点经度
     * @param string $field_lat 目标纬度字段
     * @param string $field_long 目标经度地段
     * @param string $alias 新字段别名
     * @return string
     */
    function distance_line_raw(string $latitude,string $longitude,string $field_lat='latitude',string $field_long='longitude',string $alias="distance"){
        return "st_distance_sphere(point($field_long,$field_lat),point({$longitude},{$latitude})) ".($alias?'as distance ':'');
    }
}

if (!function_exists('format_bytes')) {
    /**
     * 格式化字节大小
     * @param  number $size      字节数
     * @param  string $delimiter 数字和单位分隔符
     * @return string            格式化后的带单位的大小
     */
    function format_bytes($size, $delimiter = '') {
        $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
        for ($i = 0; $size >= 1024 && $i < 5; $i++) {
            $size /= 1024;
        }

        return round($size, 2) . $delimiter . $units[$i];
    }
}

if(!function_exists('hide_mobile')){
    /**
     * 隐藏手机号中间4位
     * @param string $mobile
     * @return string
     */
    function hide_mobile(string $mobile){
        return mb_substr($mobile,0,3)."****".mb_substr($mobile,7,11);
    }
}

if(!function_exists('hide_admin_frame')){
    /**
     * 快速隐藏后台页面的菜单、头部
     * 也可以使用 dcat自带的隐藏彩带 return $content->full()->body(view(‘welcome’));
     * https://learnku.com/docs/dcat-admin/2.x/required-reading-before-development/8083#c9c542
     */
    function hide_admin_frame(){
        $style=<<<EOF
    .content-wrapper{
           margin-left:0!important;
        margin-top:0!important;
        padding-top:0!important;
    }
    .main-sidebar,.main-footer,.main-header,#app .content-header{
    display: none!important;
    }
EOF;
        \Dcat\Admin\Admin::style($style);
    }

}

if(!function_exists('append_admin_frame')){
    /**
     * 在后台页面加载完后，向框架列表、表单页面的新增，删除，编辑按钮追加自定义参数
     * @param array $params
     */
    function append_admin_frame(array $params){
        $str=http_build_query($params);
        $js=<<<EOF
$(".pull-right a").each(function(){
    $(this).attr("href",$(this).attr("href")+"?{$str}");
});
$(".grid__actions__ .dropdown-item a").each(function(){
    $(this).attr("href",$(this).attr("href")+"?{$str}");
});
$(".pagination a.page-link").each(function(){
    $(this).attr("href",$(this).attr("href")+"?{$str}");
});

EOF;
        \Dcat\Admin\Admin::script($js);
    }
}
if (!function_exists("aes_encrypt")) {
    /**
     * aes加密
     * @param object $str [需要加密的字符串或数组或对象]
     * @param string|null $key 16位key
     * @param string|null $iv 16位IV
     * @return object     加密后的字符串
     * @author MrGuo
     * @date   2021-01-09
     */
    function aes_encrypt($str, string $key=null, string $iv=null) {
        if(empty($key))$key=env("AES_KEY",'');
        if(empty($iv))$iv=env("AES_IV",'');
        if (gettype($str) == "array" || gettype($str) == "object") {
            $str = json_encode($str, JSON_UNESCAPED_UNICODE);
        }
        $encrypted = base64_encode(openssl_encrypt($str, "AES-128-CBC", $key, true, $iv));
        return $encrypted;
    }

    /**
     * aes解密
     * @param string $data 要解密的字符串
     * @param string|null $key 16位key
     * @param string|null $iv 16位IV
     * @return object           解密后的内容，会尝试json_decode
     * @author MrGuo
     * @date   2021-01-09
     */
    function aes_decrypt(string $data, string $key=null, string $iv=null) {
        if(empty($key))$key=env("AES_KEY",'');
        if(empty($iv))$iv=env("AES_IV",'');
        $decrypted = openssl_decrypt(base64_decode($data), "AES-128-CBC", $key, true, $iv);

        if (json_decode($decrypted, true)) {
            return json_decode($decrypted, true);
        } else {
            return $decrypted;
        }
    }
    /* js 解密 代码:
    import CryptoJS from './cryptojs/crypto-js.js';
    import Config from '../../config.js';
    var crypto = {
      //默认采用 CBC  Pkcs7
      encrypt:function(str){
        if(typeof(str)=="object"||typeof(str)=="array"){
          str=JSON.stringify(str);
        }else{
          str=str.toString();
        }
        let key=CryptoJS.enc.Utf8.parse(Config.aes_key);
        let iv=CryptoJS.enc.Utf8.parse(Config.aes_iv);
        var encrypted = CryptoJS.AES.encrypt(str,key,{
          iv:iv,
          mode:CryptoJS.mode.CBC,
          padding:CryptoJS.pad.Pkcs7,
        });
        return encrypted.toString();
      },
      decrypt:function(str) {
        let key=CryptoJS.enc.Utf8.parse(Config.aes_key);
        let iv=CryptoJS.enc.Utf8.parse(Config.aes_iv);

        var decrypted = CryptoJS.AES.decrypt(str,key,{
          iv:iv,
          padding:CryptoJS.pad.Pkcs7
        });
        return decrypted.toString(CryptoJS.enc.Utf8);
      }

    }
    module.exports = crypto;
    */
}
